import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mediapipe_task_vision/interactive_segmentation.dart';
import 'dart:ui' as ui;

class MaskWidget extends StatefulWidget {
  final image;

  const MaskWidget(this.image, {super.key});

  @override
  State<MaskWidget> createState() => _MaskWidgetState();
}

class _MaskWidgetState extends State<MaskWidget> {
  Map? _mask;
  var path;
  var bytes;
  var image;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    path = widget.image;
    Size size = MediaQuery.of(context).size;
    return Scaffold(
        appBar: AppBar(),
        body: Builder(builder: (c) {
          return InkWell(
              onTapDown: (d) {
                segment(d.localPosition.dx, d.localPosition.dy);
              },
              child: Stack(children: [
                Visibility(
                    visible: bytes == null, child: Image.file(File(path))),
                _mask != null
                    ? CustomPaint(
                        size: size,
                        painter: SelectPainter(_mask!["mask"],
                            _mask!["maskWidth"], _mask!["maskHeight"]))
                    : Container(),
                bytes != null
                    ? CustomPaint(
                        size: size,
                        painter: RemoveBgPainter(
                            bytes,_mask!["mask"], _mask!["maskWidth"], _mask!["maskHeight"]))
                    : Container(),
              ]));
        }));
  }

  Future segment(double touchX, double touchY) async {
    List<Keypoint> scribble = [];
    scribble.add(Keypoint(x:touchX, y:touchY));
    Map? res = await InteractiveSegmentationApi().segment(path, scribble) as Map;
    print(res);
    if (res != null) {
      setState(() {
        _mask = res;
      });
      scaleImage();
    }
  }

  Future scaleImage() async {
    ImmutableBuffer buffer = await ImmutableBuffer.fromFilePath(path);
    final codec = await instantiateImageCodecFromBuffer(
      buffer,
      targetHeight: _mask!["maskHeight"],
      targetWidth: _mask!["maskWidth"],
    );
    image = (await codec.getNextFrame()).image;
    ByteData? byteData =
    await image.toByteData(format: ImageByteFormat.rawRgba);
    bytes = byteData!.buffer.asUint8List();
    setState(() {

    });
    print("img_len:" + bytes.length.toString());
    // print("image_len:" + bytes.length.toString());
    // this.image = await decodeImageFromList(bytes);
  }
}

class ImagePainter extends CustomPainter {
  final ui.Image image;

  ImagePainter(this.image);

  @override
  void paint(Canvas canvas, Size size) {
    double scale = size.width / image.width;
    canvas.scale(scale);
    canvas.drawImage(image, Offset.zero, Paint());
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

class SelectPainter extends CustomPainter {
  final Uint8List maskData;
  final int width;
  final int height;

  SelectPainter(this.maskData, this.width, this.height);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint();
    double scale = size.width / width;
    paint.color = const Color(0xFF84FFFF).withOpacity(0.5);
    print("mask_len:" + maskData.length.toString());
    for (var i = 0; i < maskData.length; i++) {
      var category = maskData[i];
      if (category.round() == 0) {
        int x = (i + 1) % width;
        double y = (i + 1 - x) / width;
        canvas.drawRect(
            Rect.fromLTWH(x.toDouble() * scale, y * scale, 1, 1), paint);
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

class MaskPainter extends CustomPainter {
  final Uint8List maskData;
  final int width;
  final int height;

  MaskPainter(this.maskData, this.width, this.height);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint();
    double scale = size.width / width;
    for (var i = 0; i < maskData.length; i++) {
      var category = maskData[i];
      if (category.round() == 0) {
        paint.color = Colors.white;
      } else {
        paint.color = Colors.black;
      }
      int x = (i + 1) % width;
      double y = (i + 1 - x) / width;
      canvas.drawRect(
          Rect.fromLTWH(x.toDouble() * scale, y * scale, 1, 1), paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class RemoveBgPainter extends CustomPainter {
  final Uint8List mask;
  final Uint8List bytes;
  final int width;
  final int height;

  RemoveBgPainter(this.bytes,this.mask,  this.width, this.height);

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint();
    print("img_len:" + bytes.length.toString());
    double scale = size.width / width;
    for (var i = 0; i < mask.length; i++) {
      var category = mask[i];
      if (category.round() == 0) {
        int x = (i + 1) % width;
        double y = (i + 1 - x) / width;
        paint.color =
            Color.fromRGBO(bytes[4 * i], bytes[4 * i + 1], bytes[4 * i + 2], 1);
        canvas.drawRect(Rect.fromLTWH(x.toDouble()*scale, y*scale, 1, 1), paint);
      }
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}
